﻿
using System;
using System.Collections.Generic;
using Xunit;
using Xunit.Extensions;

namespace EmperialApps.WeatherSpark.Data {

    public class TestScale {

        [Theory]
        [InlineData( 0.0, 5, 12 )]
        [InlineData( 0.1, 5, 13 )]
        [InlineData( 0.2, 4, 13 )]
        public void ctor_GivenDataMargin_UsesRoundedDataRange( double dataMargin, double minimum, double maximum ) {
            var expected = Tuple.Create( minimum, maximum );
            var data = new[] { 10.2, 5.1, 8.6, 12 };

            var scale = new Scale( false, 0, data, dataMargin );
            var actual = Tuple.Create( scale.Minimum, scale.Maximum );

            Assert.Equal( expected, actual );
        }

        [Theory]
        [InlineData( 0, 1, 1, new double[] { 0, 1 } )]
        [InlineData( -2, 4, 2, new double[] { -2, 0, 2, 4 } )]
        [InlineData( 0.5, 1.8, 0.5, new double[] { 0.5, 1.0, 1.5 } )]
        public void GetRangeValues_GivenValidRange_ReturnsAllRangeValues( double start, double end, double increment, double[] expected ) {
            var actual = Scale.GetRangeValues( start, end, increment );

            Assert.Equal( expected, actual );
        }

        [Theory]
        [PropertyData( RelativeValues_Name )]
        public void ToRelative_GivenDataValue_ReturnsRelativeValue( bool isInverted, double value, double expected ) {
            var scale = GetScale( isInverted );

            double actual = scale.ToRelative( value );

            Assert.Equal( expected, actual );
        }

        [Theory]
        [PropertyData( RelativeValues_Name )]
        public void ToScreen_GivenDataValue_ReturnsScreenValue( bool isInverted, double value, double relative ) {
            double expected = relative * ScreenSize;
            var scale = GetScale( isInverted );

            double actual = scale.ToScreen( value );

            Assert.Equal( expected, actual );
        }

        [Theory]
        [PropertyData( RelativeValues_Name )]
        public void FromScreen_GivenScreenValue_ReturnsDataValue( bool isInverted, double value, double relative ) {
            double screen = relative * ScreenSize;
            var scale = GetScale( isInverted );

            double actual = scale.FromScreen( screen );

            Assert.Equal( value, actual, 14 );
        }


        #region Utility

        private const double ScreenSize = 100;

        private const string RelativeValues_Name = "RelativeValues";
        public static IEnumerable<object[]> RelativeValues {
            get {
                yield return new object[] { false, 0, 0.0 };
                yield return new object[] { false, 2, 0.2 };
                yield return new object[] { false, 10, 1.0 };
                yield return new object[] { true, 0, 1.0 };
                yield return new object[] { true, 2, 0.8 };
                yield return new object[] { true, 10, 0.0 };
            }
        }

        private static Scale GetScale( bool isInverted ) {
            var scale = new Scale( isInverted, ScreenSize, new double[] { 0, 10 } );
            return scale;
        }

        #endregion
    }

}
